package org.sqlproc.engine.impl.type;

import java.lang.reflect.Method;

import org.hibernate.Hibernate;
import org.hibernate.Query;
import org.hibernate.type.Type;
import org.joda.time.DateTime;
import org.joda.time.LocalDateTime;
import org.sqlproc.engine.SqlRuntimeException;
import org.sqlproc.engine.impl.BeanUtils;

/**
 * The SQL META Types for DATETIME.
 * 
 * @author <a href="mailto:Vladimir.Hudec@gmail.com">Vladimir Hudec</a>
 */
public class SqlDateTimeType extends SqlGenericType {

    /**
     * {@inheritDoc}
     */
    @Override
    public Type getHibernateType() {
        return Hibernate.TIMESTAMP;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Class<?>[] getClassTypes() {
        return new Class[] { java.util.Date.class };
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String[] getMetaTypes() {
        return new String[] { "DATETIME" };
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setResult(Object resultInstance, String attributeName, Object resultValue, boolean ingoreError)
            throws SqlRuntimeException {
        if (logger.isTraceEnabled()) {
            logger.trace(">>> setResult " + getMetaTypes()[0] + ": resultInstance=" + resultInstance
                    + ", attributeName=" + attributeName + ", resultValue=" + resultValue);
        }
        Method m = BeanUtils.getSetter(resultInstance, attributeName, java.util.Date.class);
        if (m != null) {
            if (resultValue instanceof java.sql.Timestamp) {
                ((java.sql.Timestamp) resultValue).setNanos(0);
                BeanUtils.simpleInvokeMethod(m, resultInstance, resultValue);
            } else if (resultValue instanceof java.util.Date) {
                BeanUtils.simpleInvokeMethod(m, resultInstance, resultValue);
            } else if (ingoreError) {
                logger.error("Incorrect datetime " + resultValue + " for " + attributeName + " in " + resultInstance);
            } else {
                throw new SqlRuntimeException("Incorrect datetime " + resultValue + " for " + attributeName + " in "
                        + resultInstance);
            }
        } else {
            m = BeanUtils.getSetter(resultInstance, attributeName, LocalDateTime.class);
            if (m != null) {
                LocalDateTime dt = new LocalDateTime(resultValue);
                BeanUtils.simpleInvokeMethod(m, resultInstance, dt);
            } else if (ingoreError) {
                logger.error("There's no getter for " + attributeName + " in " + resultInstance + ", META type is "
                        + getMetaTypes()[0]);
            } else {
                throw new SqlRuntimeException("There's no setter for " + attributeName + " in " + resultInstance
                        + ", META type is " + getMetaTypes()[0]);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setParameter(Query query, String paramName, Object inputValue, Class<?> inputType, boolean ingoreError)
            throws SqlRuntimeException {
        if (logger.isTraceEnabled()) {
            logger.trace(">>> setParameter " + getMetaTypes()[0] + ": paramName=" + paramName + ", inputValue="
                    + inputValue + ", inputType=" + inputType);
        }
        if (inputValue == null) {
            query.setParameter(paramName, inputValue, Hibernate.TIMESTAMP);
        } else {
            DateTime dt = new DateTime(inputValue).withMillisOfSecond(0);
            query.setTimestamp(paramName, dt.toDate());
        }
    }
}
